﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace LabColorPicker {

    public partial class OneDimensionalSliceControl: UserControl, INotifyPropertyChanged {

        //
        // Interface
        //

        // Constructor

        public OneDimensionalSliceControl( ) {
            InitializeComponent( );
        }

        // Properties

        public new double Height {
            get { return base.Height; }
            set {
                lock ( _lockObject ) {
                    base.Height = value;
                    Slice.Height = base.Height - Slice.Margin.Top - Slice.Margin.Bottom;
                    SetMarkerPositionFromValue( );
                }
                _OnPropertyChanged( "Height" );
            }
        }

        public new double Width {
            get { return base.Width; }
            set {
                lock ( _lockObject ) {
                    base.Width = value;
                    Slice.Width = base.Width - Slice.Margin.Left - Slice.Margin.Right;
                    SetMarkerPositionFromValue( );
                }
                _OnPropertyChanged( "Width" );
            }
        }

        public ImageSource Source {
            get { return Slice.Source; }
            set {
                Slice.Source = value;
                _OnPropertyChanged( "Source" );
            }
        }

        private double _maximum = 255d;
        public double Maximum {
            get { return _maximum; }
            set {
                lock ( _lockObject ) {
                    _maximum = value;
                    _range = _maximum - _minimum;
                }
                _OnPropertyChanged( "Maximum" );
                _OnPropertyChanged( "Range" );
            }
        }

        private double _minimum = 0d;
        public double Minimum {
            get { return _minimum; }
            set {
                lock ( _lockObject ) {
                    _minimum = value;
                    _range = _maximum - _minimum;
                }
                _OnPropertyChanged( "Minimum" );
                _OnPropertyChanged( "Range" );
            }
        }

        private double _range = 255d;
        public double Range {
            get { return _range; }
        }

        private double _value;
        public double Value {
            get { return _value; }
            set {
                lock ( _lockObject ) {
                    _value = value;
                    SetMarkerPositionFromValue( );
                }
                _OnValueChanged( );
                _OnPropertyChanged( "Value" );
            }
        }

        // Methods

        public void SetMinimumAndMaximum( double minimum, double maximum ) {
            lock ( _lockObject ) {
                _minimum = minimum;
                _maximum = maximum;
                _range = _maximum - _minimum;

                SetMarkerPositionFromValue( );
            }

            _OnPropertyChanged( "Minimum" );
            _OnPropertyChanged( "Maximum" );
            _OnPropertyChanged( "Range" );
        }

        public void SetMinimumMaximumAndValue( double minimum, double maximum, double value ) {
            lock ( _lockObject ) {
                _minimum = minimum;
                _maximum = maximum;
                _range = _maximum - _minimum;
                _value = value;

                SetMarkerPositionFromValue( );
            }

            _OnValueChanged( );
            _OnPropertyChanged( "Minimum" );
            _OnPropertyChanged( "Maximum" );
            _OnPropertyChanged( "Range" );
            _OnPropertyChanged( "Value" );
        }

        // Events

        public event EventHandler ValueChanged;
        private void _OnValueChanged( ) {
            var handlers = ValueChanged;
            if ( null != handlers ) {
                handlers( this, EventArgs.Empty );
            }
        }

        //
        // Implementation
        //

        private readonly object _lockObject = new object( );

        protected double MarkerPosition = 0d;

        protected void SetValueFromMarkerPosition( double pt ) {
            if ( !IsLoaded ) {
                return;
            }

            lock ( _lockObject ) {
                var pixelYRange = Slice.ActualHeight - 1d;

                var newValue = MathX.Clamp( pt, pixelYRange );

                MarkerPosition = newValue;

                _value = MathX.Translate( MathX.Scale( pixelYRange - newValue, pixelYRange, _range ), _minimum );
            }

            UpdateMarkers( );

            _OnValueChanged( );
            _OnPropertyChanged( "Value" );
        }

        protected void SetMarkerPositionFromValue( ) {
            if ( !IsLoaded ) {
                return;
            }

            lock ( _lockObject ) {
                var pixelYRange = Slice.ActualHeight - 1d;

                MarkerPosition = MathX.Clamp( MathX.Scale( MathX.Translate( Value, -_minimum ), _range, pixelYRange ), pixelYRange );
            }

            UpdateMarkers( );
        }

        protected void UpdateMarkers( ) {
            LeftArrow.MoveTo( 0, MarkerPosition + 1 );
            RightArrow.MoveTo( Slice.Margin.Left + Slice.ActualWidth, MarkerPosition + 1 );
        }

        // Event handlers

        private void UserControl_Loaded( object sender, RoutedEventArgs ev ) {
            SetMarkerPositionFromValue( );
        }

        private void Slice_LeftButtonDown( object sender, MouseButtonEventArgs ev ) {
            ev.Handled = true;

            var source = (UIElement) sender;
            source.MouseLeftButtonUp += Slice_LeftButtonUp;
            source.MouseMove += Slice_MouseMove;
            source.CaptureMouse( );

            SetValueFromMarkerPosition( ev.GetPosition( Slice ).Y );
        }

        private void Slice_LeftButtonUp( object sender, MouseEventArgs ev ) {
            ev.Handled = true;

            var source = (UIElement) sender;
            source.ReleaseMouseCapture( );
            source.MouseLeftButtonUp -= Slice_LeftButtonUp;
            source.MouseMove -= Slice_MouseMove;

            SetValueFromMarkerPosition( ev.GetPosition( Slice ).Y );
        }

        private void Slice_MouseMove( object sender, MouseEventArgs ev ) {
            if ( !Slice.IsMouseCaptured || Keyboard.Modifiers != ModifierKeys.None ) {
                return;
            }
            ev.Handled = true;

            SetValueFromMarkerPosition( ev.GetPosition( Slice ).Y );
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void _OnPropertyChanged( string propertyName ) {
            var handlers = PropertyChanged;
            if ( null != handlers ) {
                handlers( this, new PropertyChangedEventArgs( propertyName ) );
            }
        }

        #endregion

    }

}
